﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace ICodeShare.UI.Controls
{
    /// <summary>
    /// 一个下方带辐射阴影的边框
    /// A border that also shows a radial shadow below it.
    /// </summary>
    [TemplatePart(Name = PART_Shadow, Type = typeof(Ellipse))]
    [TemplatePart(Name = PART_ShadowInnerStop, Type = typeof(GradientStop))]
    [TemplatePart(Name = PART_ShadowOuterStop, Type = typeof(GradientStop))]
    [TemplatePart(Name = PART_ShadowTranslate, Type = typeof(TranslateTransform))]
    [TemplatePart(Name = PART_ShadowScale, Type = typeof(ScaleTransform))]
    public class RadialShadowBorder : ContentControl
    {
        #region Constants
        private const string PART_Shadow = "PART_Shadow";
        private const string PART_ShadowInnerStop = "PART_ShadowInnerStop";
        private const string PART_ShadowOuterStop = "PART_ShadowOuterStop";
        private const string PART_ShadowTranslate = "PART_ShadowTranslate";
        private const string PART_ShadowScale = "PART_ShadowScale";
        #endregion

        #region Fileds

        /// <summary>
        /// The inner shadow gradient stop.
        /// </summary>
        private GradientStop shadowInnerStop;

        /// <summary>
        /// The outer shadow gradient stop.
        /// </summary>
        private GradientStop shadowOuterStop;

        /// <summary>
        /// Stores the shadow translate transform.
        /// </summary>
        private TranslateTransform shadowTranslate;

        /// <summary>
        /// Stores the shadow scale;
        /// </summary>
        private ScaleTransform shadowScale;

        /// <summary>
        /// Stores the shadow.
        /// </summary>
        private Ellipse shadow;

        #endregion

        #region Constructors
        static RadialShadowBorder()
        {
            //设置用于引用此控件的样式，方式一
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RadialShadowBorder), new FrameworkPropertyMetadata(typeof(RadialShadowBorder)));
        }

        /// <summary>
        /// Initialises a new instance of the <see cref="RadialShadowBorder"/> class.
        /// </summary>
        public RadialShadowBorder()
        {
            this.SizeChanged += new SizeChangedEventHandler(this.OnSizeChanged);
        }

        #endregion

        #region Properties

        #region Dependency Properties

        #region ShadowColor 辐射阴影颜色

        /// <summary>
        /// 获取或设置辐射阴影颜色 Gets or sets the radial shadow colour.
        /// </summary>
        [Category("Appearance"), Description("The radial shadow color.")]
        public Color ShadowColor
        {
            get { return (Color)this.GetValue(RadialShadowColorProperty); }
            set { this.SetValue(RadialShadowColorProperty, value); }
        }

        /// <summary>
        /// 辐射阴影颜色属性
        /// The radial shadow colour property.
        /// </summary>
        public static readonly DependencyProperty RadialShadowColorProperty = DependencyProperty.Register(
            "ShadowColor",
            typeof(Color),
            typeof(RadialShadowBorder),
            new PropertyMetadata(Colors.Black, OnShadowColorChanged));

        /// <summary>
        /// Updates the radial shadow.
        /// </summary>
        /// <param name="dependencyObject">The radial shadow border.</param>
        /// <param name="eventArgs">Dependency Property Changed Event Args</param>
        private static void OnShadowColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            RadialShadowBorder radialShadowBorder = (RadialShadowBorder)dependencyObject;
            radialShadowBorder.UpdateShadowColor((Color)eventArgs.NewValue);
        }

        #endregion

        #region RadialShadowOpacity 辐射阴影透明度

        /// <summary>
        /// 获取或设置辐射阴影透明度 Gets or sets the radial shadow opacity.
        /// </summary>
        [Category("Appearance"), Description("The radial shadow opacity.")]
        public double RadialShadowOpacity
        {
            get { return (double)this.GetValue(RadialShadowOpacityProperty); }
            set { this.SetValue(RadialShadowOpacityProperty, value); }
        }

        /// <summary>
        /// 辐射阴影透明度属性
        /// The radial shadow opacity property.
        /// </summary>
        public static readonly DependencyProperty RadialShadowOpacityProperty = DependencyProperty.Register(
            "RadialShadowOpacity",
            typeof(double),
            typeof(RadialShadowBorder),
            new PropertyMetadata(0.56));

        #endregion

        #region RadialShadowVerticalOffset 辐射阴影纵向偏移距离

        /// <summary>
        /// 获取或设置辐射阴影纵向偏移距离
        /// Gets or sets the radial shadow distance.
        /// </summary>
        [Category("Appearance"), Description("The radial shadow vertical offset.")]
        public double RadialShadowVerticalOffset
        {
            get { return (double)this.GetValue(RadialShadowVerticalOffsetProperty); }
            set { this.SetValue(RadialShadowVerticalOffsetProperty, value); }
        }

        /// <summary>
        /// 辐射阴影纵向偏移距离属性
        /// The radial shadow distance property.
        /// </summary>
        public static readonly DependencyProperty RadialShadowVerticalOffsetProperty = DependencyProperty.Register(
            "RadialShadowVerticalOffset",
            typeof(double),
            typeof(RadialShadowBorder),
            new PropertyMetadata(0.0, RadialShadowVerticalOffset_Changed));

        /// <summary>
        /// Updates the radial shadow.
        /// </summary>
        /// <param name="dependencyObject">The radial shadow border.</param>
        /// <param name="eventArgs">Dependency Property Changed Event Args</param>
        private static void RadialShadowVerticalOffset_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            RadialShadowBorder radialShadowBorder = (RadialShadowBorder)dependencyObject;
            radialShadowBorder.UpdateShadowVerticalOffset((double)eventArgs.NewValue);
        }

        #endregion

        #region ShadowScaleX 辐射阴影横向缩放比

        /// <summary>
        /// 获取或设置辐射阴影横向缩放比
        /// Gets or sets the radial shadow width.
        /// </summary>
        [Category("Appearance"), Description("The radial shadow scaleX.")]
        public double ShadowScaleX
        {
            get { return (double)this.GetValue(ShadowScaleXProperty); }
            set { this.SetValue(ShadowScaleXProperty, value); }
        }

        /// <summary>
        /// 辐射阴影横向缩放比属性
        /// The radial shadow width property.
        /// </summary>
        public static readonly DependencyProperty ShadowScaleXProperty = DependencyProperty.Register(
            "ShadowScaleX",
            typeof(double),
            typeof(RadialShadowBorder),
            new PropertyMetadata(1.0, OnShadowScaleXChanged));

        /// <summary>
        /// Updates the radial shadow.
        /// </summary>
        /// <param name="dependencyObject">The radial shadow border.</param>
        /// <param name="eventArgs">Dependency Property Changed Event Args</param>
        private static void OnShadowScaleXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            RadialShadowBorder radialShadowBorder = (RadialShadowBorder)dependencyObject;
            radialShadowBorder.UpdateShadowScale((double)eventArgs.NewValue);
        }

        #endregion

        #region RadialShadowSpread 辐射阴影衰退速率

        /// <summary>
        /// 获取或设置辐射阴影衰退速率
        /// Gets or sets the radial shadow spread.
        /// </summary>
        [Category("Appearance"), Description("The radial shadow spread.")]
        public double RadialShadowSpread
        {
            get { return (double)this.GetValue(RadialShadowSpreadProperty); }
            set { this.SetValue(RadialShadowSpreadProperty, value); }
        }

        /// <summary>
        /// 辐射阴影衰退速率属性
        /// The radial shadow spread property.
        /// </summary>
        public static readonly DependencyProperty RadialShadowSpreadProperty = DependencyProperty.Register(
            "RadialShadowSpread",
            typeof(double),
            typeof(RadialShadowBorder),
            new PropertyMetadata(0.0, RadialShadowSpread_Changed));

        /// <summary>
        /// Updates the radial shadow.
        /// </summary>
        /// <param name="dependencyObject">The radial shadow border.</param>
        /// <param name="eventArgs">Dependency Property Changed Event Args</param>
        private static void RadialShadowSpread_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            RadialShadowBorder radialShadowBorder = (RadialShadowBorder)dependencyObject;
            radialShadowBorder.UpdateStops((double)eventArgs.NewValue);
        }

        #endregion

        #region CornerRadius 边框圆角

        /// <summary>
        /// 获取或设置边框圆角
        /// Gets or sets the border corner radius.
        /// </summary>
        [Category("Appearance"), Description("Sets the corner radius on the border.")]
        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)this.GetValue(CornerRadiusProperty); }
            set { this.SetValue(CornerRadiusProperty, value); }
        }

        /// <summary>
        /// 边框圆角属性
        /// The corner radius property.
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
            "CornerRadius",
            typeof(CornerRadius),
            typeof(RadialShadowBorder),
            new PropertyMetadata(new CornerRadius(0.0)));

        #endregion

        #region ClipContent 当超出边框时，是否裁剪内容

        /// <summary>
        /// 获取或设置是否裁剪内容，当内容超出边框时
        /// Gets or sets a value indicating whether the content is clipped.
        /// </summary>
        [System.ComponentModel.Category("Appearance"), System.ComponentModel.Description("Sets whether the content is clipped or not.")]
        public bool ClipContent
        {
            get { return (bool)this.GetValue(ClipContentProperty); }
            set { this.SetValue(ClipContentProperty, value); }
        }

        /// <summary>
        /// 是否裁剪内容属性
        /// The clip content property.
        /// </summary>
        public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register(
            "ClipContent",
            typeof(bool),
            typeof(RadialShadowBorder),
            new PropertyMetadata(true));

        #endregion

        #endregion

        #endregion

        #region Methods

        #region 重写方法 

        /// <summary>
        /// Gets the parts out of the template.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.shadow = GetTemplateChild(PART_Shadow) as Ellipse;
            this.shadowInnerStop = GetTemplateChild(PART_ShadowInnerStop) as GradientStop;
            this.shadowOuterStop = GetTemplateChild(PART_ShadowOuterStop) as  GradientStop; 
            this.shadowTranslate = GetTemplateChild(PART_ShadowTranslate) as TranslateTransform;
            this.shadowScale = GetTemplateChild(PART_ShadowScale) as ScaleTransform;

            this.UpdateShadowColor(this.ShadowColor);
            this.UpdateShadowSize(new Size(this.ActualWidth, this.ActualHeight));
            this.UpdateStops(this.RadialShadowSpread);
            this.UpdateShadowScale(this.ShadowScaleX);
            this.UpdateShadowVerticalOffset(this.RadialShadowVerticalOffset);
        }

        #endregion

        /// <summary>
        /// Updates the shadow size.
        /// </summary>
        /// <param name="newSize">The new control size.</param>
        internal void UpdateShadowSize(Size newSize)
        {
            if (this.shadow != null)
            {
                this.shadow.Height = newSize.Height / 3;
                this.shadow.Margin = new Thickness(0, 0, 0, -this.shadow.Height / 2);
            }
        }

        /// <summary>
        /// Updates the shadow scale;
        /// </summary>
        /// <param name="scaleX">The scale X of the shadow.</param>
        internal void UpdateShadowScale(double scaleX)
        {
            if (this.shadowScale != null)
            {
                this.shadowScale.ScaleX = scaleX;
            }
        }

        /// <summary>
        /// Updates the gradient stops offset.
        /// </summary>
        /// <param name="spread">The spread of the stops.</param>
        internal void UpdateStops(double spread)
        {
            if (this.shadowInnerStop != null)
            {
                this.shadowInnerStop.Offset = spread;
            }
        }

        /// <summary>
        /// Updates the shadow colour.
        /// </summary>
        /// <param name="color">The new shadow colour.</param>
        internal void UpdateShadowColor(Color color)
        {
            if (this.shadowInnerStop != null)
            {
                this.shadowInnerStop.Color = color;
            }

            if (this.shadowOuterStop != null)
            {
                this.shadowOuterStop.Color = Color.FromArgb(0, color.R,color.G,color.B);
            }
        }

        /// <summary>
        /// Updates the vertical offset of the shadow.
        /// </summary>
        /// <param name="verticalOffset">The vertical offset.</param>
        internal void UpdateShadowVerticalOffset(double verticalOffset)
        {
            if (this.shadowTranslate != null)
            {
                this.shadowTranslate.Y = Math.Max(0, this.RadialShadowVerticalOffset);
            }
        }

        /// <summary>
        /// Updates the radial shadow size.
        /// </summary>
        /// <param name="sender">The radial shadow border.</param>
        /// <param name="e">Size changed event args.</param>
        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.UpdateShadowSize(e.NewSize);
        }

        #endregion
    }
}